赞
踩
进程是出于运行过程中的程序,具有一定的独立功能
进程是系统进行资源分配和调度的一个独立的单位
进程是正在运行的程序,进程负责给程序分配内存
class Demo extends Thread{
@Override
public void run(){
System.out.println("这是创建的线程任务");
}
public satic void main(String[] args){
//开启线程任务
Demo demo = new Demo();
demo.start();
}
}
name
相同。实现接口
class Demo01 implement Runnable{
public void run(){
System.out.println("这是创建的线程任务");
}
public satic void main(String[] args){
//开启线程任务
Demo01 demo = new Demo01();
new Thread(demo).start();
}
}
匿名内部类
class Dmeo02{
public static void main(String[] args){
new Thread(()->{
System.out.println("这是创建的线程任务");
}).start();
}
}
通过继承Callable接口
实现call方法
class CallableDemo implements Callable<Integer>{
@Override
public Integer call() throws Exception{
int sum = 0 ;
for(int i = 0 ; i < 100 ; i ++){
sum += i;
System.out.println("使用Callable接口创建子线程"+i);
}
return sum ;
}
}
我们实现Callable 之后,我们发现这个类是没有start方法 的
此时,我们就需要借助FutureTask这个类
这个类它实现了Future接口
而Future接口继承了我们的Runnable 和Callable 接口
此时,我们就可以通过如下方法,创建这个线程(多态的原理)
class Test{
public static void main(String[] args){
CallableDemo demo = new CallableDemo();
FutureTask ft = new FutureTask(demo);
new Tread(ft).start();
//我们可以通过get方法来获取这个返回值
try{
Integer sum = (Integer)ft.get();
System.out.println("Callable子线程返回:"+sum);
}catch(Expection e){
e.printStacjTrace();
}
}
}
class Demo extends Thread { private int count ; @Override public void run(){ for(int i = 0 ; i < 10 ; i ++){ count ++ ; } System.out.println(count); } public static void main(String[] args){ Demo demo = new Demo(); Demo demo1 = new Demo(); demo.start(); demo1.start(); } } /*执行输出: 10 10 */
class Demo implements Runnable{ private int count ; @Override public void run(){ for(int i = 0 ; i < 10 ; i ++){ count ++ ; } System.out.println(count); } public static void main(String[] args){ Demo demo = new Demo(); Demo demo1 = new Demo(); new Thread(demo).start(); new Thread(demo1).start(); } } /*执行输出: 7 7 多次执行发现,没次结果都不大相同 */
通过对上方的两个程序结果对比
我们发现
继承Thread 的线程对象,不能够共享数据
实现Runnable 的线程对象,使用不同的Thread对象,同时执行一个实现Runnable接口的对象,则会出现共享数据的现象,可能会出现线程安全问题.
通过加锁,解决
synchronized(key){
//同步代码,key必须是一个对象
}
class Demo implements Runnable{ private int count ; Object obj = new Object(); @Override public void run(){ for(int i = 0 ; i < 10 ; i ++){ synchronized(obj){ count ++ ; } } System.out.println(count); } public static void main(String[] args){ Demo demo = new Demo(); Demo demo1 = new Demo(); new Thread(demo).start(); new Thread(demo1).start(); } } /*运行结果 10 20 */
如果某个方法都是可能出现线程安全问题
则建议加在方法上
该方法在充当锁的钥匙
class Demo implements Runnable{
private int count ;
Object obj = new Object();
class Demo implements Runnable{ private int count ; @Override public synchronized void run(){ for(int i = 0 ; i < 10 ; i ++){ count ++ ; } System.out.println(count); } public static void main(String[] args){ Demo demo = new Demo(); Demo demo1 = new Demo(); new Thread(demo).start(); new Thread(demo1).start(); } } /*运行结果 10 20 */
此时充当钥匙的是,静态方法所在的静态类
类.class 可以拿到当前类的字节码对象
synchronized 同步锁在jdk7之前,默认调用系统锁(重量级锁)效率慢
基于如上原因,jdk5,JUC包诞生,提供了一个Lock锁
lock锁,是jdk5.0提供的锁,是一个可重入锁(ReentrantLock)
可以充当公平锁,也可以充当不公平锁
一旦加锁,最后必须释放锁,否则会出现死锁现象
将释放锁的代码写入finally中
class Demo implements Runnable{ private int count ; private Lock lock = new ReentrantLock(); @Override public void run(){ for(int i = 0 ; i < 10 ; i ++){ try{ //手动上锁 lock.lock(); count ++ ; //必须写入try中 }finally{ //手动解锁 lock.unlock(); } } System.out.println(count); } public static void main(String[] args){ Demo demo = new Demo(); Demo demo1 = new Demo(); new Thread(demo).start(); new Thread(demo1).start(); } } /*运行结果 10 20 */
两个线程之间,进行资源竞争时,彼此之间拿着对方需要的资源,但是因为线程的竞争性(请求保存),形成一种相互等待对方释放资源的现象,这种现象就叫死锁
死锁只能尽量避免,不能完全解决
我们在编程中,会遇到多个地方使用同一个对象的情况
而为了避免对象的重复创建
我们有两种单例模式: 饿汉式,懒汉式
class Demo{
//已经创建好的静态对象
private static Demo demo = new Demo();
//私有化我们的构造函数
private Demo(){}
//提供一个访问器
public Demo newDemo(){
return this.demo;
}
}
此时我们在外部就可以通过访问器,来访问这个已经创建好的对象
class Demo{
//定义一个对象变量
private static Demo demo = null;
//私有化我们的构造函数
private Demo(){}
//提供一个访问器
public Demo newDemo(){
//判断是否是第一次访问
if(demo==null){
demo = new Demo();
}
return this.demo;
}
}
对于懒汉式来说,在单线程模式下,是没有问题的,但是在多线程下他就会出现很大的问题,
例如说,a通过访问器获取对象,此时demo==null,当程序正要创建对象时,b抢占了资源,此时demo依旧为空
为了避免这种情况,我们可以使用双重检查锁
class Demo{ //定义一个对象变量 private static Demo demo = null; //私有化我们的构造函数 private Demo(){} //提供一个访问器 public Demo newDemo(){ //判断是否是第一次访问 if(demo==null){ //加锁 synchronized(Demo.class){ //双重判断 if(demo==null){ demo = new Demo(); } } } return this.demo; } }
双重检查锁,并不是一定有效的
在编译过程中,代码优化可能会改变我们的代码顺序
因此,我们必须禁止代码优化
因此,正确的双重检查锁的形式应该如下
class Demo{ //定义一个对象变量 private static volatile Demo demo = null; //私有化我们的构造函数 private Demo(){} //提供一个访问器 public Demo newDemo(){ //判断是否是第一次访问 if(demo==null){ //加锁 synchronized(Demo.class){ //双重判断 if(demo==null){ demo = new Demo(); } } } return this.demo; } }
唤醒机制必须配合 同步锁
一个简单的供需平衡问题
生产者
package ThreadDemo; import java.util.Random; /** * @author : YWJ * @date : 2022/11/14 : 20:46 */ public class Product extends Thread{ private String[] foods ; private Disk disk ; private Random random ; public Product(Disk disk) { this.foods = new String[]{"牛肉面","兰州拉面","削筋面","担担面","重庆小面","浆水面","油泼面"}; this.disk = disk ; this.random = new Random(); } @Override public void run() { makeFood(); } private void makeFood() { synchronized (this.disk) { for (int i = 0; i < 10; i++) { if (this.disk.isFull()) { //创造食物 String food = this.foods[this.random.nextInt(this.foods.length)]; System.out.println("生产了一个食物:" + food); this.disk.setFood(food); this.disk.setFull(false); //唤醒消费者 this.disk.notify(); //让生产者进入等待 try { this.disk.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } else { //让生产者进入等待 try { this.disk.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }
package ThreadDemo; /** * @author : YWJ * @date : 2022/11/14 : 20:46 */ public class Customer extends Thread { private Disk disk ; public Customer(Disk disk,String name) { super(name); this.disk = disk; } @Override public void run() { synchronized (this.disk) { while (true) { if (!this.disk.isFull()) { String food = this.disk.getFood(); System.out.println(currentThread().getName() + "消费了一次 :" + food); this.disk.setFull(true); this.disk.notify(); try { this.disk.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else { try { this.disk.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }
package ThreadDemo; /** * @author : YWJ * @date : 2022/11/14 : 20:44 */ public class Disk { public String getFood() { return food; } public void setFood(String food) { this.food = food; } public boolean isFull() { return full; } public void setFull(boolean full) { this.full = full; } private String food; private boolean full = true; }
package ThreadDemo; /** * @author : YWJ * @date : 2022/11/14 : 21:14 */ public class TestCP { public static void main(String[] args) { Disk disk = new Disk(); Product product = new Product(disk); Customer customer = new Customer(disk,"张三"); //将消费者设置成守护线程 customer.setDaemon(true); product.start(); customer.start(); } }
生产了一个食物:削筋面
张三消费了一次 :削筋面
生产了一个食物:重庆小面
张三消费了一次 :重庆小面
生产了一个食物:削筋面
张三消费了一次 :削筋面
生产了一个食物:削筋面
张三消费了一次 :削筋面
生产了一个食物:油泼面
张三消费了一次 :油泼面
生产了一个食物:担担面
张三消费了一次 :担担面
生产了一个食物:担担面
张三消费了一次 :担担面
生产了一个食物:削筋面
张三消费了一次 :削筋面
生产了一个食物:削筋面
张三消费了一次 :削筋面
生产了一个食物:担担面
张三消费了一次 :担担面
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。