1.什么是线程
线程是进程的一个单元片段,多个线程组合到一起,完成一个进程。
2.创建线程方式
第一种创建方式
1.extends Thread类,这个类是在java.lang包下。这个类代表的就是线程。
2.继承Thread类时,必须重写run方法,run方法中定义的是我们的线程要执行的内容.
3.启动一个线程要调用Thread类中的start方法。
以下例子就是这种创建方式
//线程创建
public class five extends Thread {
public static void main(String[] args) {
//第一种方式:继承Therad类,重写run
five td = new five(); //创建一个线程
td.run(); //调用run方法,不是线程
td.start(); //启动线程
}
//重写run方法
@Override
public void run() {
System.out.println("hello run");
}
}
以上代码在执行时:
1.程序加载到jvm后,jvm会辟一条通道,去运行main线程。
2.main线程执行,它会执行main方法中的内容,这时会创建一个ThreadDemo1对象,
这个对象就是一个线程对象,
3.通过这个对象调用了start方法,这时jvm会在开辟一条通道去运行run方法中的内容。
4.也就是说,对于当前这个程序,
它有两个通道,一个是main通道,一个是td通道。
第二种创建方式
1.implements Runnable(推荐)
2.在启动线程时使用的是:new Thread(实现了Runnable接口的类的对象).start();
以下例子就是这种创建方式
//第二种实现线程方法(接口)
public class seven implements Runnable {
public static void main(String[] args) {
//开启线程
seven s = new seven();
new Thread(s).start();
for (int i = 0; i < 10; i++) {
System.out.println("main"+i);
}
}
//线程要执行的内容
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("run"+i);
}
}
}
注意:线程在执行时,它具有无序性。
3.线程执行状态
1.创建
2.运行
3.结束
4.冻结
5.临时堵塞
4.线程安全问题
1.出现安全问题条件
1.多线程.
2.多个线程共享数据.
3.共享的数据被多条语句操作
为了更好阐述死锁,下面将使用一个例子来说明。
//演示死锁 public class thirteen { public static void main(String[] args) { Thread2 mt = new Thread2(); //创建2个线程 Thread th1 = new Thread(mt); Thread th2 = new Thread(mt); //自定义线程名 th1.setName("first"); th2.setName("second"); //开启线程 th1.start(); th2.start(); } } class Thread2 implements Runnable { @Override public void run() { // TODO Auto-generated method stub //线程内容 if (Thread.currentThread().getName().equals("first")) { first(); } else { second(); } } public void first() { synchronized(Globle.obj1) { try { Thread.sleep(1); //休眠1秒,得到obj1锁 } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } synchronized(Globle.obj2) { //发现th2线程拿着这把锁,等待obj2释放 System.out.println("first"); } } } public void second() { synchronized(Globle.obj2) { //th2线程,得到obj2这把锁向下执行 synchronized(Globle.obj1) { //只有得到obj1这把锁才可向下执行 System.out.println("second"); } } } } class Globle { //以下2个对象作为锁对象 public static final Object obj1 = new Object(); public static final Object obj2 = new Object(); }
2.解决安全问题:锁机制
要锁定代码第一种:synchronized块(同步块)
synchronized(任意对象){
}
锁对象就是 this
注意:多个线程所使用的锁对象必须是同一个。
以下例子就是这种锁机制
//线程安全,用锁解决 public class twelve { public static void main(String[] args) { Thread1 mt = new Thread1(); // 创建两个线程 Thread th1 = new Thread(mt); Thread th2 = new Thread(mt); //开启线程 th1.start(); th2.start(); } } class Thread1 implements Runnable { int count = 10; //共享数据 public void run() { //使用是main函数类中的对象 synchronized(this) { count++; try { Thread.sleep(1); //线程睡眠1秒 //因为th1线程使用了同步块,这时,th2线程等待,th1线程休眠结束,继续执行 } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } System.out.println(count); //th1线程结束。释放了锁。这时th2才可以执行。 } } }
第二种:synchronized方法(同步方法)
synchronized做为一个修饰符去修饰方法。
1.非static方法。 锁对象就是 this
2.static方法 锁对象就是 类名.class.
以下例子就是这种锁机制
总结:static 同步函数,使用的锁不是this,而是字节码文件对象, 类名.class//线程安全,用锁解决 public class twelve { public static void main(String[] args) { Thread1 mt = new Thread1(); // 创建两个线程 Thread th1 = new Thread(mt); Thread th2 = new Thread(mt); //开启线程 th1.start(); th2.start(); } } class Thread1 implements Runnable { static int count = 10; //共享数据 @Override public void run() { count++; synchronized(Thread1.class) { //当前同步代码块所使用的锁是本类的对象 try { Thread.sleep(1); //休眠1秒 } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } System.out.println(count); } } }