------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
一、概述
1、进程:正在执行的程序,每一个进程都会有一个执行顺序,称为执行单元。
2、线程:进程中包含线程,是进程中的一个独立的执行单元,一个在执行的进程至少有一个线程。
3、多线程:就是一个程序能够执行多个任务。这些任务都是由里面的线程来执行的。如:JVM执行的时候就是一个多线程的,里面包至少包含主线程和垃圾回收的线程。多线程的存在大大的提高了程序的效率。
二、创建线程
可以通过两种方式来创建线程。
1、继承Thread类, 并重写run()方法, 调用start()方法来开启线程
2、实现Runnable接口, 并重写run()方法, 开启的方法是:创建实现类的对象,new Thread(实现类对象).start()
3、俩者的区别:
避免了JAVA单继承的局限性, 在定义线程的时候尽量使用实现的方式
实现Runnable接口: 代码存放在实现接口的类的run()方法中
继承Tread类: 代码存放在Tread类的子类的run()方法中
4、示例
class Test extends Thread{
@Override
public void run() {
System.out.println("使用继承方式创建线程");
}
}
class Test2 implements Runnable{
@Override
public void run() {
System.out.println("使用实现接口方式创建线程");
}
}
public class TheardDemo {
public static void main(String[] args) {
//使用继承方式创建线程的开启方式
Test t = new Test();
t.start();
//使用实现接口的方式创建线程的开启方式
Test2 t2 = new Test2();
Thread thread = new Thread(t2);
thread.start();
}
}
三、线程的状态
四、多线程同步
当多条语句操作同一线程共享数据时,一个线程对多条语句只执行到一部分代码,而另外一个线程就参与进来执行,导致共享
数据错误。
解决:当多个线程操作同一个数据时,让一个线程先执行完,再执行其他的线程
synchronized(){
需要同步的代码
}
同步的两个前提:1、必须有两个或者两个以上的线程;2、必须是多个线程使用同一个锁
优缺点:优点:解决多线程安全问题;缺点:多线程需要判断锁,较为消耗资源
class Demo2 implements Runnable {
private int t = 100;
Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (obj) {
try {
Thread.sleep(10);
} catch (Exception e) {
}
if (t <= 0)
return;
System.out.println(Thread.currentThread().getName() + " -> " + t--);
}
}
}
}
同步函数,注意:同步函数使用的锁为this
在函数上加上关键字synchronized,示例:
class MyTicket implements Runnable {
private int tick = 100;
//同步函数
public synchronized void show() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " -> " + tick--);
}
@Override
public void run() {
while(tick>0){
show();
}
}
}
静态函数同步,注意静态函数同步的锁是对象文件的class
示例:
//懒汉式
class Single{
private static Single s = null;
private Single(){
}
public static Single getInstance(){
if(null==s){
synchronized(Single.class){
if (null==s) {
s=new Single();
}
}
}
return s;
}
}
五、线程死锁
就是同步中嵌套同步。
示例:
class MyTest implements Runnable {
private boolean flag;
public MyTest(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
synchronized (MyLock.lockA) {
System.out.println("if lockA");
synchronized (MyLock.lockB) {
System.out.println("if lockB");
}
}
} else {
synchronized (MyLock.lockB) {
System.out.println("else lockB");
synchronized (MyLock.lockA) {
System.out.println("else lockA");
}
}
}
}
}
class MyLock {
static Object lockA = new Object();
static Object lockB = new Object();
}
public class DeadLockTest {
public static void main(String[] args) {
new Thread(new MyTest(true)).start();
new Thread(new MyTest(false)).start();
}
}
六、线程间通讯
多个线程同时操作同一块资源,只是操作的方式不同
class Res{
public String name;
public String sex;
public boolean flag = false;
}
class Input implements Runnable{
private Res res;
public Input(Res res) {
this.res=res;
}
@Override
public void run() {
int f =0;
while (true) {
synchronized (res) {
if(res.flag)
try {
res.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
if(f == 0){
res.name="tom";
res.sex="man";
}else{
res.name="吉米";
res.sex="女";
}
f=(f+1)%2;
res.flag=true;
res.notify();
}
}
}
}
class Output implements Runnable{
private Res res;
public Output(Res res) {
this.res=res;
}
@Override
public void run() {
while(true){
synchronized (res) {
if(!res.flag)
try {
res.wait(); //等待, 放弃CPU执行权, 释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(res.name + " -----> " + res.sex);
res.flag=false;
res.notify(); //唤醒
}
}
}
}
public class InputOutputDemo {
public static void main(String[] args) {
Res res = new Res();
Input in = new Input(res);
Output out = new Output(res);
new Thread(in).start();
new Thread(out).start();
}
}
//notifyAll() 唤醒等待池的所有线程
//wait()和notify() 都是要对持有监视器(锁)的线程进行操作, 都是object中的方法, 因为锁可以是任意对象, 所以这个方法定义在最上层的类中
//等待和唤醒必须在同一个锁中进行.
七、停止线程
在JDK1.5以前有stop()方法可以用来停止线程, 但在这个版本之后就舍弃了这个 方法。开启多线程运行, 通常代码都是循环的, 所以只要控制循环, 就可以让run()方法结束, 就可以让线程结束 线程同步时, 可能挺不下来, 解决如下
特殊情况: 当线程处于冻结状态, 就不会读取标记, 线程就不会结束
但没有指定方式让冻结的线程恢复到运行状态时, 这时需要对冻结进行清除, 强制让线程恢复到运行中来, 这样就可以操作标记让线程结束
Thread中提供了该方法, iterrupt()
示例:
class StopThread implements Runnable{
private boolean flag = true;
@Override
public synchronized void run() {
while(flag){
try {
wait();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " -> Excption");
flag = false;
}
System.out.println(Thread.currentThread().getName() + " <---");
}
}
public void changeFlag(){
this.flag = false;
}
}
public class StopThreadDemo {
public static void main(String[] args) {
StopThread s1 = new StopThread();
Thread t1 = new Thread(s1);
Thread t2 = new Thread(s1);
//设置为守护线程, setDaemon()方法必须在开启线程之前调用
//当设置为守护线程的时候, 主线程结束, 那么守护线程也随之结束
t1.setDaemon(true);
t2.setDaemon(true);
t1.start();
t2.start();
int num = 0;
while(true){
if(num++ == 60){
//s1.changeFlag();
t1.interrupt();
t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName() + " -> " + num);
}
System.out.println("over");
}
}
yield()方法和join()方法,示例:
class Demo3 implements Runnable{
@Override
public void run() {
for(int i=0;i<40;i++){
System.out.println(Thread.currentThread().getName() + " -> " + i);
Thread.yield(); //暂停当前正在执行的对象, 并执行其他的线程
}
}
}
public class JoinDemo {
public static void main(String[] args) throws InterruptedException {
Demo3 d = new Demo3();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t1.join(); //当A线程执行到了b线程的.join()方法时,A线程就会等待,等B线程都执行完,A线程才会执行。(此时B和其他线程交替运行。)join可以用来临时加入线程执行。
t2.start();
for(int i = 0;i<80;i++){
System.out.println("main -> " + i);
}
System.out.println("over");
}
}