一:概述:
1.1什么是进程,什么是线程:
1.2进程和线程的关系:
进程A和进程B内存空间独立不共享,线程A和线程B堆内存和方法区内存共存,但是栈内存,一个线程一个栈。(堆和方法区共享,栈内存独立)
二:实现线程的方法:
第一种:编写一个新类,直接继承java.lang.thread;并且重写run方法
public class Yunxing2 { public static void main(String[] args) { MyThread myThread = new MyThread();//新建线程 myThread.start();//启动新线程 //作用:启动一个分支线程,在JVM中开辟一个新的栈空间,任务完成后,瞬间就结束了 //启动成功后,自动调用run方法,并且run方法跟main方法一样在栈空间的底部,同级别的 //这里的代码还是运行在主线中 myThread.run();//不用这个 //作用L不会启动线程,不会分配新的分支栈中,是单线程的run()方法 for(int i=0;i<1000;i++){ System.out.println("逐鹿线---->"+i); } } } //继承+重写run方法,main中new出一个该类,然后使用start启动一个新的线程 class MyThread extends Thread{//定义线程类 @Override public void run() { //编写程序,这段程序运行在分支线程中(分支) for(int i=0;i<1000;i++) { System.out.println("分支线程---->"+i); } } }
第二种方法:编写一个类,实现java.lang.Runnable 接口(这种方法更好,因为一个类实现了一个接口,还可以继承别的类)
//定义一个可运行的类 public class MyRunnable implements Runnable{ public void run(); } //创建一个线程对象Thread,并且传一个runnable参数 Thread t= new Thread(new MyRunnable()); //启动线程 t.start();
第三种方法:匿名内部类直接编写分支线程代码:
//main函数中直接写就好 Thread t=new Thread(new Runnable(){//在这里直接实现,这就是匿名内部类 @Override public void run(){ for(int i=0;i<100;i++){ System.out.println("t线程---->"+i); } } }); t.start();//启动支线线程 for(int i=0;i<100;i++){ System.out.printn("main线程--->"+i); }
三:线程生命周期:
新建状态->就绪状态->运行状态->阻塞状态->死亡状态
四:线程的一些操作:
-
获取并且修改线程的名字
//用第一种创造线程的方式: MyThread t= new MyThread(); MyThread.setname("新线程"); String tname=MyThread.getname();
-
获取当前线程对象:(有点类似于this指针)
//获取:static Thread currentThread(); //调用:Thread Thread.currrentThread(); Thread t=Thread.currentThread(); System.out.println(t.getname+"--->"+123123); //如果这段代码出现在主线程中,当前线程就是主线程, //当t1线程执行该代码,就是t1线程 //当t2线程执行该代码,就是t2线程
-
线程Sleep方法:
/* static void sleep(long millis); 1.静态方法:Thread.sleep(1000); 2.参数:毫秒 3.作用:让当前线程进入休眠《进入“阻塞状态”,放弃占用CPU时间片 4.Thread.sleep();可以像计时器那样,间隔记录时间 */ Thread t=new MyThread(); t.start(); try{ t.sleep(millis:1000*5); }catch(InterruptedException e){ e.printStaclTrace(); }
-
中断睡眠方式:interrupt,但是这种终断睡眠的方式是依靠java的异常处理机制实现的
public static void main(String[] args) { Thread t=new Thread(new MyRunnable2()); t.setName("t"); t.start(); //必须要有try,catch异常机制 try { Thread.sleep(1000*5); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } t.interrupt();//这里中断支线程的sleep睡眠 } //这个放在外面就好了,不要放在包创建的class里面 class MyRunnable2 implements Runnable{ @Override public void run() { // TODO Auto-generated method stub try { Thread.sleep(1000*60*60*30); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
-
结束线程:
//直接杀死线程,直接干掉这个线程(已过时,容易丢是数据) 跟上面的终止一样,就是最后哪里变成 t.stop(); //常用的结束线程,一般就是打一个bollean b标记,如果有问题,将这个变为false,想什么时候结束线程,将该类中b的值改为false,最后来一个return ;
-
线程调度:
-
实例方法:
void setPriority(int newPriority); int getPriority()//获取线程有优先级 //最低优先级:1,最高优先级:10,默认优先级:5 //设置优先级: Thread.currentThread().setPriority(1);
-
静态方法:
static void yield()//让位方法 //暂停当前正在执行的线程对象,yield()方法不是阻塞方法,让当前线程让位,让给其他线程使用 //yield()方法的执行会让当前线程从“运行状态”回到“就绪状态” //注意:回到“就绪状态”还有可能再次强到。 //设置让位: class MyRunnable6 implements Runnable{ @Override public void run(){ for(int i=1;i<10000;i++){ //每100个让位一次 if(i%100==0){ Thread.yield();//当前线程让位 } System.out.println(Thread.currentThread().getName()+"---->"+i); } } }
-
线程合并join:同样的,join方法也是需要java异常机制的执行才可以
void join() //合并线程 class MyThread1 extends Thread{ public void doSome(){ Mythread2 t=new MyThread2(); t.join();//当前线程受阻,t线程执行 } }
-
-
多线程并发环境下,数据安全问题
-
重点:以后写代码需要在多线程并发的环境下考虑数据安全的问题
-
线程不安全的条件:满足下面三个条件就会有线程安全问题
-
多线程并发
-
有共享数据
-
共享数据有修改的行为
-
-
如何解决线程安全问题:使用线程同步机制,但是会损失一些效率,莫得办法
-
哪些数据有数据安全问题:实例变量和静态变量,局部变量没有安全问题
-
举例:编写两个线程对同一个账号读取并录入信息使用同步代码快synchronized
-
-
线程同步机制:synchronized
-
语法:一般也可以直接在构造方法的时候加入,这样就对类中其中的一种方法加上一个锁
synchronized(obj/this){ //线程同步代码快 //取款之前的余额 double before=this.getBalancce(); //取款之后的余额 double after=before-money; //读取剩下的余额的信息 this.getBalancce(); }
synchronized后面的括号中传的这数据时相当关键的,
这个数据必须时多线程共享的数据,才能达到多线程排队
写什么?
那要看什么线程的同步,或者说填写共享对象
填写this或者你想同步的那几个线程的共享对象 -
原理:java语言当中,每个对象都有一把锁,也就相当于是一个标记,而使用synchronized传递对象的时候,只能同时占有一把锁,所以其他线程只能等候占用锁来继续执行下面的代码。
-
三种写法
第一种:同步代码块:
灵活
synchronized(线程共享对象){
t同步代码快
}
第二种:在实例方法中使用synchronized
表示共享对象一定时this
并且同步代码快时整个方法体
第三种:在静态方法上使用synchronized,保证静态变量的安全
表示找类锁
类锁永远只有一把
就算创建了100个对象,类锁也只有1个public class Exam01{ public static void main(String[] args){ Myclass mc=new Myclass(); Thread t1=new MyThread(mc); Thread t2=new MyThread(mc); t1.setName("t1"); t2.setName("t2"); t1.start(); Thread.sleep(1000); t2.start(); } } class MyThread extends Thread{ private Myclass=mc; public void run(){ if(Thread.currentThread().getName().equals("t1")){ mc.dosome(); } if(Thread.currentThread().getName().equals("t2")){ mc.doother(); } } } class Myclass{ public synchronized void dosome(){//这是对象锁,如果前面加一个static,那就是类锁,不管创建了几个对象,都需要等待上一个线程执行完成,才能执行下一个步骤 System.out.println("dosome begin"); try{ Thread.sleep(1000*10); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("dosome over"); public void doother(){ System.println("doother begin"); System.println("doother over"); } } } //注意:该线程使用类锁时,dosome有synchronized,但是doother没有synchronized,所以没有排序,两个异步处理,并没有同步,所以不安全 只有当两个全都有synchronized的时候才会有类锁性质顺序
-
-
死锁:直接上手,需要会写死锁
//一般是使用synchronized的时候,使用了嵌套的方法,可能会死锁,若是在死锁途中还进行睡眠操作,那将会导致程序停留崩溃
class MyThread1 extends Thread{ Obiect o1; Object o2; public void run(){ synchronized(o1){ synchronized(o2){ } } } } class MyThread2 extends Thread{ Obiect o1; Object o2; public void run(){//这里的两个object位置互换了一下 synchronized(o2){ synchronized(o1){ } } } } //以上代码,在main类中同步调用的时候,也就是MyThread1.start();MyThread2.start();会出现死锁现象,导致程序暂停。
-
如何解决线程安全问题?(这个之后再说吧)
1. 创建局部变量,没有安全问题
2. 创建多个实例对象,实例变量的内存就不共享了
3. 啥都不能用的话,只能适合用synchronized了