emmmm…
不知不觉已经学到了线程了 没写过博客 不过很多人都推荐写博客 所以就试试喽
1.线程实现的方式
1.继承Thread类
废话不说先上例子
public class Demo1 {
//首先在类中方法外定义一个静态类使其继承Thread
static class MyThread extends Thread {
@Override
//覆写Thread中的run方法
public void run() {
for (int i = 0; i < 10; i++) {//使用一个10次的循环
System.out.println("这是第" + (i + 1) + "次执行");//输出第几次执行
}
}
}
public static void main(String[] args) {
new MyThread().start();//启动线程
}
}
启动线程得到结果
这是第1次执行
这是第2次执行
这是第3次执行
这是第4次执行
这是第5次执行
这是第6次执行
这是第7次执行
这是第8次执行
这是第9次执行
这是第10次执行
程序中我们可以清晰的看到是一个类继承自Thread,那么我们知道继承的父类只能有一个,当我们继承自Thread后就无法继承别的类或者是我们继承自一个类就无法继承Thread那么java开发者又给了我们一种选择就是Runnable接口
2.实现Runnable接口
打开Runnable类的源代码我们可以看到Runnable接口中只定义了一个run方法 所以我们在实现Runnable接口时只需要覆写run方法
public class Demo1 {
//创建MyRunnable实现Runnable接口
static class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {//使用一个10次的循环
System.out.println("这是第" + (i + 1) + "次执行");//输出第几次执行
}
}
}
public static void main(String[] args) {
//创建MyRunnable对象
MyRunnable myRunnable = new MyRunnable();
//创建线程
new Thread(myRunnable).start();
}
}
2.线程操作
1. 线程休眠
使用sleep时无法抛出异常只能用try catch捕捉
Thread.sleep(1000);//执行线程休眠方法
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
2.线程放弃
使当前线程主动放弃时间片
Thread.yield();//线程主动放弃时间片
当创建两个线程后输出结果:
这是第1次执行Thread-0
这是第1次执行Thread-1
这是第2次执行Thread-0
这是第2次执行Thread-1
这是第3次执行Thread-0
这是第3次执行Thread-1
这是第4次执行Thread-0
这是第4次执行Thread-1
这是第5次执行Thread-0
这是第5次执行Thread-1
这是第6次执行Thread-0
这是第6次执行Thread-1
这是第7次执行Thread-0
这是第7次执行Thread-1
这是第8次执行Thread-0
这是第8次执行Thread-1
这是第9次执行Thread-0
这是第9次执行Thread-1
这是第10次执行Thread-0
这是第10次执行Thread-1
可以看出输出的结果呈很明显的交叉分布
3.线程加入
允许其他线程加入到当前线程中
thread.join();//当前线程加入主线程
当一个线程调用join方法时,该线程阻塞其他线程的执行,当该线程运行完毕之后其他线程才会执行,但该线程必须调用启动方法
当存在两个线程调用join方法时这两个线程依旧会抢占执行,然后等这两个线程执行完毕之后其他线程才会执行
4.设置线程优先级
线程优先级Priority
t1.setPriority(6);//设置线程优先级
- 首先线程优先级分为1-10共有10个等级
- 我们在启用start而不设置线程优先级的时候默认的优先级为5
t1.join();
t1.setPriority(9);//这样写会失去效用
- join和设置线程优先级不能同时使用否则会失去效用即join的阻塞效果以及线程的优先效果都会失去
t2.join();
t1.setPriority(9);
- 当两个线程一个调用join 一个设置线程优先级 则线程也是抢占执行
- 在这里两个join与setPriority都是抢占执行 why?
5.设置守护线程
- 守护线程是和主线程(用户线程)一起结束的线程
- 首先守护线程会随着主线程的结束而强制结束(无论守护线程有没有执行完毕)
t1.setDaemon(true);//将t1设置为守护线程
6.线程锁的使用
同步代码块
-
当我们运行线程的时候 有些情况:例如在上一个线程还没有结束的时候下一个线程已经开始执行,而上一个线程的执行结果会影响到下一个线程的数据,此时就会出现线程安全问题,在这种情况之下我们就引进了线程锁
-
static class MyThread implements Runnable{ //首先创建锁 private Object obj = new Object(); private int i = 100; @Override public void run() { //加入同步块 while(true){ //这里就是同步代码块 synchronized(obj){ if (i<=0) { break; } System.out.println(); i--; } } } }
-
在这里介绍的只是synchronized锁的一个应用 也是性能相对较高的应用方式
-
当然synchronized锁也可以使用在静态方法上
-
也可以使用在实例方法中
同步静态方法
public class Demo2 {
static class MyThread implements Runnable {
private static int n=100;
//创建synchronized锁修饰的静态方法
public static synchronized void cls() throws InterruptedException {
for (int i=0;i<100;i++) {
Thread.sleep(200);
n--; System.out.println(Thread.currentThread().getName()+"............."+n);
}
}
@Override
public void run() {
try {
MyThread.cls();//调用被synchronized修饰的静态方法
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
//创建两个线程进行测试
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
Thread t1 = new Thread(myThread1);
Thread t2 = new Thread(myThread2);
t1.setName("线程1");
t2.setName("线程2");
t2.start();
t1.start();
}
}
- 运行此程序 也就是利用synchronized锁修饰的静态方法
- synchronized修饰静态方法相当于锁的是当前类也就是相当于synchronized(MyThread.class)
- 由于锁的是 MyThread 类所以无论你创建了多少个这个类的对象 无论线程执行的是哪个对象当我们执行线程的时候都会线程安全
- 通过结果分析到当调用start方法t2 t1抢时间片 当一个线程抢到时间片另一个线程就会被synchronized锁阻塞待 这个线程执行完毕另一个线程才会执行
- 也就是说当synchronized锁应用在静态方法上时且线程的作用区间是这个类的类对象 会限制单个线程运行
同步方法
public class Demo1 {
private static int i=100 ;
static class MyThread implements Runnable{
public synchronized void cls(){//synchronized锁普通类
for (int n =0;n<50;n++) {
if (i<=0)break;
System.out.println(Thread.currentThread().getName()+"......"+i);
i--;
}
}
@Override
public void run() {
cls();
}
}
public static void main(String[] args) {
MyThread myThread1 = new MyThread();
Thread t1 = new Thread(myThread1);
Thread t2= new Thread(myThread1);
Thread t3 = new Thread(myThread1);
t1.setName("线程1");
t2.setName("线程2");
t3.setName("线程3");
t1.start();
t2.start();
t3.start();
}
}
当我们新建一个MyThread对象的时候
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
Thread t1 = new Thread(myThread1);
Thread t3 = new Thread(myThread2);
t1.setName("线程1");
t3.setName("线程3");
t1.start();
t3.start();
类似于这样 就会发现t1与t3线程进行抢占执行 而且线程不安全
由此可见当锁普通的方法时synchronized只能锁创建的类对象 当两个线程作用的类对象不相同 而需要进行线程安全设置时 锁也就失去了意义
- synchronized锁普通类相当于synchronized(this)
- 而这里的this相当于当前类对象myThread1
- 当执行t1线程的时候上锁 t1线程执行完毕 下锁继续执行下一个线程 当下一个线程开始执行 上锁 执行完毕 下锁 以此类推
3.线程通信
1.等待
使用在对obj加锁的同步代码块中,在一个线程中,调用obj.wait()时,此线程会释放所拥有的所有锁标记。同时此线程阻塞在o的等待队列中。释放锁进入等待队列
//两种使用方法
public final void wait()
public final void wait(long timeout)
2.通知
利用通知方法来唤醒等待线程
//两种唤醒方法
public final void notify()
public final void notifyAll()