单线程和多线程
多线程的利弊:
- 利:多线程可以提高程序运行效率和资源的利用率。
- 弊:多线程会比较消耗资源,效率比较低。处理不好的话会造成线程死锁。
代码演示单线程和多线程
public class ThreadDemo extends Thread {
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 5; i++) {
try {
sleep(1000);// 睡一秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "一共卖出了" + i + "张票");
}
}
public static void main(String[] args) {
ThreadDemo threadDemo1 = new ThreadDemo();
ThreadDemo threadDemo2 = new ThreadDemo();
threadDemo1.setName("一号售卖机");
threadDemo2.setName("二号售卖机");
/*
* 调用方法(单线程)
*/
threadDemo1.run();
threadDemo2.run();
/*
* start启动线程(多线程)
*/
threadDemo1.start();
threadDemo2.start();
}
}
多线程的实现方法
- 继承Thread类
从Thread类中实例化的对象即代表线程,启动一个线程就是建立一个Thread实例。因为完成线程真正功能的代码放在类的run()方法中,所以可以将线程要做的事写在run()方法中即可。然后调用Thread类中的start()方法执行线程,也就是调用run()方法。
public class ThreadDemo extends Thread {
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 5; i++) {
try {
sleep(1000);// 睡一秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "一共卖出了" + i + "张票");
}
}
public static void main(String[] args) {
ThreadDemo threadDemo1 = new ThreadDemo();
ThreadDemo threadDemo2 = new ThreadDemo();
threadDemo1.setName("一号售卖机");
threadDemo2.setName("二号售卖机");
/*
* start启动线程(多线程)
*/
threadDemo1.start();
threadDemo2.start();
}
}
- 实现Runnable接口(推荐使用)
Java不支持多重继承(推荐的原因),因此如果有一个子类要想实现线程,那就可以实现Runnable接口。实现了Runnable接口并编写run()方法,使该任务可执行你的命令。
public class ThreadDemo2 implements Runnable {
@Override
public void run() {
for (int j = 0; j < 5; j++) {
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "一共卖出了" + j + "张票");
}
}
public static void main(String[] args) {
ThreadDemo2 t1 = new ThreadDemo2();
ThreadDemo2 t2 = new ThreadDemo2();
Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t2);
thread1.setName("一号售卖机");
thread2.setName("二号售卖机");
thread1.start();
thread2.start();
}
}
- 实现Callable接口
Callable接口类似于Runnable接口,实现起来较为复杂,和Runnable接口有些不同,最主要的区别是Callable接口要实现的call()方法中可以抛出异常,且还可以在任务执行后返回值。
多线程安全问题
俗话说:有利必有弊。
多线程提高程序运行效率和资源的利用率的同时也会遇到安全问题。
我们肯定要做到资源在同一时间只能有一个线程访问。
如何解决?
- 我们可以采用同步方法,用synchronized修饰方法,整个方法的代码都是同步的,只能一个线程运行。同步方法使用this作为锁。
synchronized(xxx.class){
中间是要同步的代码
}
- 同步锁
Lock lock =new ReentrantLock();
lock.lock();
try {
/*
* 同步代码块
*/
} finally {
lock.unlock();
}
线程通信
使用锁对象的notify()方法可以将正在等待的线程唤醒,但是同时有多个线程都处于等待状态,notify()只是随机唤醒一个。
总结
-
线程同步的目的是为了保护资源,防止多个线程访问同一个资源时破坏了资源。
-
线程同步方法是通过锁来实现,每个对象都有且仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其它访问该对象的线程就无法再访问该对象的其他同步方法。
-
对于同步,要时刻清楚在哪个对象上同步,这是关键。
-
当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。
-
使用lock()来代替synchronized时,一定要解锁并注意unlock()的位置