- 程序:一段静态代码。
- 进程:正在执行的程序。一个进程可以有多个线程。进程是操作系统资源分配的基本单位。
- 线程:程序内部的一条执行路径。线程是处理器任务调度和执行的基本单位。
- 并行:多CPU同时执行多个任务
- 并发:一个CPU”同时“执行多个任务
- 提高CPU利用率;
- 提高应用程序的响应,增强用户体验;
- 改善程序结构。
- start():启动当前线程,调用run()方法
- run():通常需要重写,将创建的线程要执行的操作声明在此方法中
- currentThread():返回当前代码的线程
- getName():获取当前线程名
- setName():设置当前线程名
- yield():释放当前线程CPU控制权
- join():线程a调用线程b的join(),此时线程a进入阻塞状态,直至线程b完全执行完毕,线程a阻塞状态才结束。
- sleep(long millitime):是当前线程睡眠指定时间
- isAlive():判断当前线程是否存活
方法一:继承Thread类
- 创建一个继承于Thread类的子类
- 重写run()方法,将此线程操作声明在run()中;
- 创建子类对象
- 通过此对象调用start()
class MyThread extends Thread{
@Override
public void run() {
for (int i=0;i<20;i++){
if (i%2==0){
System.out.println(i);
}
}
}
}
public class test {
public static void main(String[] args) {
MyThread myThread = new MyThread();
//start()作用:启动当前线程;调用当前线程的run();
myThread.start();
for (int i=0;i<20;i++){
if (i%2==0){
System.out.println("**** "+i+" ****");
}
}
}
}
方法二:实现Runnable接口
- 创建一个实现Runnable接口的类
- 实现类实现Runnable接口的抽象方法:run()
- 创建实现类的对象
- 将此对象作为参数传递到Thread类的构造器中,创建Thread类对象
- 通过Thread类的对象调用start()
class MyThread implements Runnable{
@Override
public void run() {
for (int i=0;i<20;i++){
if (i%2==0){
System.out.println(Thread.currentThread().getName()+i);
}
}
}
}
public class test {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
for (int i=0;i<20;i++){
if (i%2==0){
System.out.println(Thread.currentThread().getName()+"**** "+i+" ****");
}
}
}
}
方法三:实现Callable接口
与Runnable相比,Callable功能更加强大
- run()方法可以有返回值
- 方法可以抛出异常
- 支持泛型的返回值
- 需要借助FutureTask类,如获取返回结果
//1.创建一个实现Callable的实现类
class MyThread implements Callable{
//2.实现call方法,将此线程执行的操作声明在此;
@Override
public Object call() throws Exception {
int sum = 0;
for (int i = 0;i<100;i++){
if (i%2==0){
System.out.println(i);
sum+=i;
}
}
return sum;
}
}
public class test {
public static void main(String[] args) {
//3.创建实现类对象
MyThread myThread = new MyThread();
//4.将此对象传递到FutureTask中
FutureTask futureTask = new FutureTask(myThread);
//5.将FutureTask的对象传递到Thread中
Thread thread = new Thread(futureTask);
thread.start();
//6.获取call方法返回值
try {
Object o = futureTask.get();
System.out.println("总和为"+o);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
方法四:使用线程池
提前创建多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。
好处:
- 提高响应速度(减少创建新线程的时间)
- 降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
- 便于线程管理
class MyThread implements Runnable{
@Override
public void run() {
for (int i = 0;i<20;i++){
System.out.println(i);
}
}
}
public class test {
public static void main(String[] args) {
//1提供指定数量的线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
//2执行线程操作
executorService.execute(new MyThread());
//Callable
//executorService.submit(Callable callable);
//3关闭线程池
executorService.shutdown();
}
}
线程优先级:
线程优先等级:
- MAX_PRIORITY:10
- MIN_PRIORITY:1
- NORM_PRIORITY:5
- getPriority():获取当前线程优先级
- setPriority(int p):设置线程的优先级
优点:解决了线程安全问题
缺点:效率低。操作同步代码时,只能有一个线程参与,其他线程等待。
方法一:同步代码块
synchronized(同步监视器){
//需要被同步的代码
}
- 操作共享数据的代码,即需要被同步的代码
- 共享数据:多个线程共同操作的变量
- 同步监视器,俗称锁。任何一个类的对象都可以充当锁。多个线程必须要共用同一把锁。
①使用同步代码块解决实现Runnable接口的线程安全问题
class MyThread implements Runnable{
private int num = 60;
@Override
public void run() {
while (true) {
synchronized (this) {
if (num > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + num);
num--;
} else {
break;
}
}
}
}
}
public class test {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread1 = new Thread(myThread);
Thread thread2 = new Thread(myThread);
Thread thread3 = new Thread(myThread);
thread1.setName("1号");
thread2.setName("2号");
thread3.setName("3号");
thread1.start();
thread2.start();
thread3.start();
}
}
②使用同步代码块解决继承Thread类的线程安全问题
class MyThread extends Thread{
private static int num = 60;
@Override
public void run() {
while (true) {
synchronized (MyThread.class) {
if (num > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + num);
num--;
} else {
break;
}
}
}
}
}
public class test {
public static void main(String[] args) {
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
MyThread thread3 = new MyThread();
thread1.setName("1号");
thread2.setName("2号");
thread3.setName("3号");
thread1.start();
thread2.start();
thread3.start();
}
}
方法二:同步方法
如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步。
- 同步方法任然涉及同步监视器,只是不需要显式声明
- 非静态的同步方法,同步监视器是this
- 静态的同步方法,同步监视器是当前类本身
①使用同步方法解决实现Runnable接口的线程安全问题
class MyThread implements Runnable{
private int num = 60;
@Override
public void run() {
while (true) {
show();
if (num ==0){
break;
}
}
}
private synchronized void show(){
if (num > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + num);
num--;
}
}
}
public class test {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread1 = new Thread(myThread);
Thread thread2 = new Thread(myThread);
Thread thread3 = new Thread(myThread);
thread1.setName("1号");
thread2.setName("2号");
thread3.setName("3号");
thread1.start();
thread2.start();
thread3.start();
}
}
②使用同步方法解决继承Thread类的线程安全问题
class MyThread extends Thread{
private static int num = 60;
@Override
public void run() {
while (true) {
show();
if (num ==0){
break;
}
}
}
private static synchronized void show(){
if (num > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + num);
num--;
}
}
}
public class test {
public static void main(String[] args) {
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
MyThread thread3 = new MyThread();
thread1.setName("1号");
thread2.setName("2号");
thread3.setName("3号");
thread1.start();
thread2.start();
thread3.start();
}
}
方法三:Lock
ReentrantLock类实现了Lock,拥有与synchronized相同的并发性和内存语义。
class MyThread implements Runnable{
private int num = 60;
//1.实例化ReentrantLock
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
//2.调用锁定方法lock()
lock.lock();
if (num > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + num);
num--;
}else {
break;
}
} finally {
//3.调用解锁方法:unlock()
lock.unlock();
}
}
}
}
public class test {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread1 = new Thread(myThread);
Thread thread2 = new Thread(myThread);
Thread thread3 = new Thread(myThread);
thread1.setName("1号");
thread2.setName("2号");
thread3.setName("3号");
thread1.start();
thread2.start();
thread3.start();
}
}
synchronized与Lock的异同:
相同点:
- 二者都可以解决线程安全问题
不同点:
- synchronized机制在执行完相应的同步代码后,自动释放同步监视器;Lock需要手动启动同步(lock()),手动结束(unlock())
- Lock只有代码块锁,synchronized有代码锁和方法锁
- 使用Lock锁,JVM将花费更少的时间来调度线程,性能更好,并且具有更好的扩展性。
public class SingletonTest {
public static void main(String[] args) {
User user1 = User.getUser();
User user2 = User.getUser();
System.out.println(user1 == user2);
}
static synchronized class User{
//1.私有化构造器
private User(){
}
//2.内部创建类的对象
//4.要求此对象的声明也必须是静态的
private static User user = null;
//3.提供公共的方法,返回类的对象
public static User getUser(){
if (user == null){
user = new User();
}
return user;
}
}
}
死锁问题
不同线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了死锁。
出现死锁后,既不会出现异常,也不会出现提示,只是所有线程都处于阻塞状态,无法继续。
解决方法:
- 尽量减少同步资源的定义
- 尽量避免嵌套同步
public class test {
public static void main(String[] args) {
StringBuffer s1 = new StringBuffer();
StringBuffer s2 = new StringBuffer();
new Thread(){
@Override
public void run() {
synchronized(s1){
s1.append("a");
s2.append("1");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(s2){
s1.append("b");
s2.append("2");
System.out.println(s1);
System.out.println(s2);
}
}
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized(s2){
s2.append("d");
s1.append("3");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(s1){
s2.append("c");
s2.append("4");
System.out.println(s1);
System.out.println(s2);
}
}
}
}).start();
}
}
线程通信
- wait():当前线程进入阻塞状态,并释放同步监视器
- notify():唤醒被wait的一个线程,如果有多个线程被wait,就唤醒优先级高的。
- notifyAll():唤醒所有被wait的线程
wait(),notify(),notifyAll()必须使用在同步代码块或同步方法中
class MyThread implements Runnable{
private int num = 30;
@Override
public void run() {
while (true) {
synchronized(this) {
notify();
if (num > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + num);
num--;
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}
}
}
}
public class test {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread1 = new Thread(myThread);
Thread thread2 = new Thread(myThread);
thread1.setName("1号");
thread2.setName("2号");
thread1.start();
thread2.start();
}
}
sleep()和wait()的异同
相同点:
- 都可以使当前线程进入阻塞状态
不同点:
- 声明位置不同。Thread类中声明sleep(),Object类中声明wait()
- 调用要求不同。sleep()可以在任何需要的场景下调用。wait()必须使用在同步代码块或同步方法中。
- 如果两个都使用在同步代码块或同步方法中。sleep()不释放同步监视器,wait()释放同步监视器。
生产者将产品交给店员,而消费者从店员处取走产品。店员只能持有一定数量的产品,到达一定数量,叫停生产者,店中有空位,再通知生产者继续生产;如果店中没有产品了,就让消费者等待,有产品再通知消费者。
class Clerk{
private int productCount = 0;
public synchronized void consumeProduct() {
if (productCount > 0){
System.out.println(Thread.currentThread().getName()+"消费第"+productCount+"个产品");
productCount--;
notify();
}else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void produceProduct() {
if (productCount < 20){
productCount++;
notify();
System.out.println(Thread.currentThread().getName()+"生产第"+productCount+"个产品");
} else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producer extends Thread{
private Clerk clerk;
public Producer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(getName()+"开始生产产品...");
while (true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.produceProduct();
}
}
}
class Consumer extends Thread{
private Clerk clerk;
public Consumer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(getName()+"开始消费产品....");
while (true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.consumeProduct();
}
}
}
public class test {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer producer = new Producer(clerk);
Consumer consumer = new Consumer(clerk);
producer.setName("生产者");
consumer.setName("消费者");
producer.start();
consumer.start();
}
}



