什么是线程
线程是比进程更小的运行单位,它被包含在进程之中,是进程实际的运行单位,一个线程是指进程单一的控制流,一个进程可以并发多个线程,每条线程并行执行不同的任务。(来源百度)
- 并发:同一时间段内,多个任务都在执行(单位时间内不一定同时执行)
-并行:单位时间内,多个任务同时执行
Java中如何创建线程
Java中创建线程的方式有三种
- 通过implement Runable 接口
- 通过extents Thread 类
- 通过Callable和Future创建线程
1.implement Runnable方式
public class threadSafe{
public void static main(String[] args){
Thread thread=new Thread(new Runnable() { //第一种方式 直接new一个Runnable 实例
@Override
public void run() {
System.out.println("this is a thread");
}
});
thread.start();
mythread mythread=new mythread();
Thread thread2=new Thread(mythread);//通过创建mythread实例创建线程
}
}
class mythread implements Runnable{
@Override
public void run() {
for(int i=0;i<10;i++){
a++;
System.out.println("running thread"+" a:"+a);
}
}
}
后两者有兴趣的同学可去菜鸟教程上学习,在这就不写了.
https://www.runoob.com/java/java-multithreading.html
什么是线程安全
一个类是线程安全的,是指被多个线程访问时,类可以持续进行正确的行为.
当多个线程访问一个对象时,如果我们不考虑线程在运行环境下的交替执行和调度,并且不需要额外的同步及在调用方代码不必做其它的协调,那么我们称这个类是线程安全的(来自java并发编程实战)
多线程中,程序的执行顺序我们是不知道的,比如什么时候执行这段代码
让我们看看实际代码中的输出
生产者
class product implements Runnable{
private int DEAFULT_CUSOTME_NUM=5;
private Thread thread;
private String ThreadName;
private goods goods;
/**
* none constructor
*/
product(){}
/**
*
* @param ThreadName
* Get threadName for this thread
*/
product(String ThreadName,goods goods){
this.ThreadName=ThreadName;
this.goods=goods;
}
public void start(){
if(thread==null){
thread =new Thread(this,ThreadName);
thread.start();
}
}
@Override
public void run() {
SimpleDateFormat formatter=new SimpleDateFormat("yyyy/MM/dd hh:mm:ss:SSS");
for(int i=0;i<5;i++){
System.out.println("thread name :" + thread.getName() + "consume goods:"
+ DEAFULT_CUSOTME_NUM + "remain goods:" +
goods.consumeGoods(DEAFULT_CUSOTME_NUM) + " current time"
+ formatter.format(new Date()) + "\n i:" + i);
; //default product five goods;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
消费者
class custome implements Runnable{
private int DEAFULT_CUSOTME_NUM=5; //default good num
private Thread thread;
private String ThreadName;
private goods goods;
/**
* none constructor
*/
custome(){}
/**
*
* @param ThreadName
* Get threadName for this thread
*/
custome(String ThreadName,goods goods){
this.ThreadName=ThreadName;
this.goods=goods;
}
public void start(){
if(thread==null){
thread =new Thread(this,ThreadName);
thread.start();
}
}
@Override
public void run() {
SimpleDateFormat formatter=new SimpleDateFormat("yyyy/MM/dd hh:mm:ss:SSS");
for(int i=0;i<5;i++){
System.out.println("thread name :"+thread.getName()
+" product goods:"+DEAFULT_CUSOTME_NUM+"remain goods:"+
goods.addGoods(DEAFULT_CUSOTME_NUM)+" current time"
+formatter.format(new Date())+"\n i:"+i); //default product five goods
}
}
main方法
public class threadSafe {
static int DEFAULT_GOODS_NUM=0;
public static void main(String[] args) {
goods goods=new goods(DEFAULT_GOODS_NUM);
custome custome2=new custome("product ",goods);
product product1=new product("consume ",goods);
custome custome1=new custome("product 1",goods);
product product2=new product("consume 2",goods);
custome2.start();
product1.start();
custome1.start();
product2.start();
}
}
我们来观察一下输出
由于我加了threadSleep,所以会按从0-5的顺序执行
虽然i:0的时候,输出顺序是0->10->5->5,结果是没有错误的,因为我们无法控制线程什么时候开始输出,比如执行操作的时候,线程一起执行,实际上读入变量顺序是这样的,produc->consumer 2-> consume->product 1
但是执行 i:1的时候,出现了问题,product 和product 1同时输出了5。说明这两个线程发生了像上面一样的图的情况,在product和product 1 执行前,先执行了consume方法,此时变量为0,当product和product1执行时,同时读入变量值0,执行addgood方法,得到了一样的结果。
这就出现了线程不安全的情况
如果将它变为线程安全
1.将goodnum设置为volatile.
为什么设置volatile有效呢,volatile的作用到底是什么
在当前java内存模型下,线程可以把变量保存到本地内存,而不是在主存中进行读写,这就可能造成一个线程在主存中修改了变量的值,而另一个线程还在使用它在寄存器中变量值的拷贝,造成数据的不一致
使用volatile声明变量,目的是告诉jvm,这个变量是不稳定的,每次使用它都应该去主存中读写
volatilel 声明的变量除了保证可见性,还能防止指令重排
2.在修改goodnum的方法中加入 synchronized
synchronized可以保证修饰的代码中,任何时刻只能有一个线程执行。
class goods {
private volatile int num;
/**
* none constructor
*/
goods(){
}
/**
*
*/
goods(int num){
this.num=num;
}
public void setNum(int num){
this.num=num;
}
public synchronized int getNum(){ //该代码块为同步代码块
return this.num;
}
public synchronized int addGoods(int num){
return this.num+=num;
}
public int consumeGoods(int num){
return this.num-=num;
}
}
网友评论