多线程
很多内容都在代码的注释中,要注意看代码的注释
优点
对单核CPU来说,多线程比单线程完成相同的任务所需时间要多(因为要进行cpu的切换)
提高程序的响应,使图形化界面更有意义(因为图形化界面要求同时完成多个任务),
提高CPU利用率
改善程序结构。将复杂的程序分成多个线程独立运行
用途
程序需要同时执行多个任务
程序需要实现一些需要等待的任务,如用户输入、文件的读写、网络操作等
需要运行后台程序
创建方式
方式一、继承Thread类
package javaSE.Threads;
/**
* 多线程的创建 方式一:继承Thread类
* 1。 创建一个继承于Thread类的子类
* 2。 重写thread类的run() --> 将此线程要做的操作写在run()中
* 3。 创建Thread类的子类对象
* 4. 通过此队形调用start()
*
* 例子:遍历100以内的偶数
*/
//创建一个Thread的子类
class MyThread extends Thread{
//重写run()
@Override
public void run() {
for (int i = 0; i <100 ; i++) {
if (i % 2 == 0){
System.out.println(i);
}
}
}
}
public class ThreadsTest {
public static void main(String[] args) {
//新建Thread子类对象
MyThread t1 = new MyThread();
//通过此对象调用start() *启动当前线程 *调用当前线程的run()
t1.start();
//t1.run();问题一 :如果直接调用run(),那么线程就根本没有被启用,run仍在主线程中执行
//t1.start();问题二:不能启动两次线程,会报IllegalThreadException 解决办法新建一个对象去start
//如下操作仍然在主线程汇总执行
for (int i = 0; i < 100; i++) {
System.out.println(i + "***********main********");
}
}
}
方法二、创建一个实现Runnable接口的类
package javaSE.Threads;
/**
* 创建线程的方法二
* 1,创建一个实现Runnable接口的类
* 2,实现接口中的run方法
* 3,创建一个实现类的对象
* 4,将此对象作为参数传递到thread类的对象,创建thread对象
* 5,通过thread类放入对象调用start方法
*
*/
class Mythread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 ==0){
System.out.println(i);
}
}
}
}
public class MyThread2 {
public static void main(String[] args) {
Mythread mythread = new Mythread();
Thread thread = new Thread(mythread);
thread.start();
//此处start启用的run是是当前线程的run方法(即thread类中的run)-->
//thread类中的run方法
/* What will be run. */
// private Runnable target;
//
// public void run() {
// if (target != null) {
// target.run();
// }
// }
}
}
jdk5.0新增线程的创建方式
方式三 实现Callable接口
实现Callable接口创建多线程比实现runable接口实现多线程方式强
1.可以有返回值
2.方法可以抛出异常
3.支持泛型的返回值
4.需借助FutureTask类,比如获取返回结果但需要Future接口
Future接口
可以对具体的Runnable、callable任务的执行结果进行取消、查询是否完成、获取结果等
FutureTask是Future接口的唯一实现类
FutureTask同时实现了Runnable、Future接口,它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
package javaSE.Threads;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 创建线程的方式三 实现Callable接口 ---- JDK 5.0新增
* 步骤
* 1, 创建一个callable实现类
* 2, 实现call()方法,将此线程需执行的操作声明在call()方法中
* 3, 创建Callable接口实现类的对象
* 4, 将Callable接口实现类的对象作为参数传递到FutureTask构造器中创建FutureTask类的对象
* 5, 将FutureTask对象作为参数传递到thread类的构造器中,创建Thread类的对象
* 6, 获取call方法的返回值
*
*
*/
class NumThread implements Callable{
@Override
public Object call() throws Exception {
int sum = 0;
for (int i = 1; i < 100; i++) {
if (i % 2 == 0){
System.out.println(i);
sum += i;
}
}
return sum;
}
}
public class ThreadNew {
public static void main(String[] args) {
NumThread numThread = new NumThread();
//创建一个FutureT类的对象
FutureTask futureTask = new FutureTask(numThread);
new Thread(futureTask).start();//如果没有这一步就不能调用重写的call方法
try {
//get()方法的返回值为FutureTask构造器参数Callable实现类重写的call()的返回值
Object sum = futureTask.get();
System.out.println(sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
方式四 线程池
[在下面]
比较
实现runnable接口比较常用,因为第一种方法采用继承类,有类的单继承局限性,而且更适合实现多线程共享数据的形式
相同点,都需要实现run方法
常用方法
package javaSE.Threads;
/**
* 测试Thread类的常用方法
* 1. start();启动当前线程,调用当前线程的run()
* 2. rnu();通常需要重写Thread类中的run()方法,将要执行的操作声明在此方法中
* 3. currentThread();静态方法,返回执行当前代码的线程
* 4. getname();获取当前线程名字
* 5. setname();设置当前线程的名字
* 6. yield();释放当前CPU的执行权
* 7. join();在线程A中调用B的join方法,此时A进入阻塞状态,直到join来的子线程执行结束后在执行A原线程
* 8. sleep(long millitime);阻塞当前线程指定时间(单位毫秒)
* 9.isAlive();判断当前线程是否存活
* 10.notify();唤醒有优先级最高的进程;notifyall();唤醒所有进程
*/
publc class ThrradsMethod {
public static void main(String[] args) {
MyThread1 mt = new MyThread1();
mt.setName("子线程");
mt.start();
Thread.currentThread().setName("主线程");
for (int i = 0; i < 100; i++) {
if(i % 2 != 0){
System.out.println(Thread.currentThread().getName() + i);
}
}
}
}
class MyThread1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName() + i);
}
}
}
}
设置线程的优先级
同优先级采用先来先服务,不同优先级采用抢占式优先级
//线程的优先级等级
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5//默认
//涉及的方法
getPriority();返回线程优先级
setPriority(int newPriority);改变线程优先级
线程的创建时继承父线程的优先级
低优先级只是获得较低的调度概率,并不一定在搞高优先级线程之后被调用
线程的同步
package javaSE.Threads;
/**
* 线程安全:例如下面,卖票系统出现错票重票问题
* 解决办法,给线程加锁实现线程的同步与互斥
*方法一: 同步代码块
* synchronized(同步监视器){
* //需要被同步的代码,即操作共享数据的代码
*
* }
*说明1.同步监视器;锁,任何一个类的对象都可以充当锁
* 要求:多个线程公用一把锁
*
*同步的好处,解决了线程的安全问题
* 局限性:操作同步代码的时候,只能有一个线程参与,效率较低
*
* 方法二;同步方法
* 如果对共享代码的操作在同一个方法中,可将该方法同步
* 同步监视器为this
*/
class Mythread implements Runnable {
private int tickets = 100;
Object object = new Object();
@Override
//public synchronized void run() {//该方式即为同步方法
public void run() {
while (true) {
synchronized (object) {//objest同步监视器,所有线程公用此监视器
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号是" + tickets);
tickets--;
} else {
break;
}
}
}
}
}
public class MyThread2 {
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();
}
}
//*******************************************8
package javaSE.Threads;
/**
* 同步方法实现继承类的同步
*/
class Mythread3 implements Runnable {
private static int tickets = 100;
Object object = new Object();
@Override
//public synchronized void run() {//该方式即为同步方法
public void run() {
while (true) {
show();
}
}
private static synchronized void show (){//同步监视器是当前的类
//private synchronized void show(){//t1,t2,t3的同步监视器是不同的,故此种方法是错误的
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号是" + tickets);
tickets--;
}
}
}
public class WindowSTest1 {
public static void main(String[] args) {
Mythread3 mythread = new Mythread3();
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();
}
}
LOCK锁
从jdk 5.0开始,java提供了更强大的线程同步机制-----通过显示定义同步锁对象来实现同步,同步锁使用lock对象充当。
ReentrantLock类实现了Lock,比较茬昂用的是ReentrantLock,因为他可以显示的加锁和释放锁
package javaSE.Threads;
import java.util.concurrent.locks.ReentrantLock;
/**
* Lock锁解决线程安全问题-----jdk 5.0之后新增
*。面试题,synchronized 和 lock的异同
* 相同:二者都可以解决线程安全问题
* 不同:synchronized机制在执行完相应的代码后自动释放同步监视而Lock方法需要手动
* 启动同步(lock()方法),同时结束同步也需要手动实现(unlock()方法)
*
* 使用顺序
* lock -> 同步代码块(已经进入了方法体,分配了相应资源) -> 同步方法(在方法体之外)
*
*
*
*/
class Windowss implements Runnable{
private static int tickets = 100;
//实例化ReentrantLock类的实例化对象
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try {
//调用Lock方法
lock.lock();
if (tickets > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "售票,票号是" + tickets);
tickets--;
}else {
break;
}
}finally {
//显示解锁
lock.unlock();
}
}
}
}
public class LockTest {
public static void main(String[] args) {
Windowss w = new Windowss();
Thread thread1 = new Thread(w);
Thread thread2 = new Thread(w);
Thread thread3 = new Thread(w);
thread1.setName("窗口一");
thread2.setName("窗口二");
thread3.setName("窗口三");
thread1.start();
thread2.start();
thread3.start();
}
}
线程通信
package javaSE.Threads;
import java.awt.print.PrinterAbortException;
/**
* 线程通信的例子,两个线程交替打印 1 - 100
* 所需方法
* wait();阻塞当前线程
* notify();唤醒一个线程,如果是多个线程就唤醒优先级锡膏的
* notifyall();唤醒全部进程
*
* 说明:
* 1. wait();notify(); notifyall();三个方法必须使用在同步方法和同步代码块中
* 2. wait();notify(); notifyall();三个方法必调用者必须是同步方法和同步代码块的
* 监视器否则会出现IllegalMonitorStateException异常
* 3. wait();notify(); notifyall();三个方法定义在object类中,以确保所有 的监视器
* 都可以调用三种方法
*
*
*/
class Number implements Runnable{
private int number = 1;
@Override
public void run() {
while (true){
synchronized (this){
notify();
if (number <= 100){
System.out.println(Thread.currentThread().getName() + ":" + number);
number++;
}else {
break;
}
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class CommunicationTest {
public static void main(String[] args) {
Number num = new Number();
Thread t1 = new Thread(num);
Thread t2 = new Thread(num);
t1.setName("甲");
t2.setName("乙");
t1.start();
t2.start();
}
}
线程池
背景:为了避免频繁的创建、销毁、使用量巨大的资源对性能的影响,提出线程池的概念
思路:提前创建好多个线程,使用时直接获取,使用完后放回池中,这样可以频繁的创建销毁,以实现重复利用
好处:
1. 提高了创建速度(减少了创建线程的时间)
-
降低了资源消耗(重复利用线程池中的线程,不需要每次都创建)
-
便于线程管理
corePoolSize 核心池的大小
maximumPoolSize: 最大线程数
keepAliveTime: 线程没有任务时最多保持多少时间终止
package javaSE.Threads;
/**
* 实现多线程的方法四 --- 线程池
* 方法
* 1.创建一个线程数为10的线程池
* 2.调用一个线程,需要提供一个实现Runnable接口或Callable接口实现类的对象
* 3.调用另一个线程
*
*/
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class NumberThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
class NumberThread1 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 != 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
public class ThreadPool {
public static void main(String[] args) {
//1.创建一个线程数为10的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//设置线程池的属性
// corePoolSize 核心池的大小
// maximumPoolSize: 最大线程数
// keepAliveTime: 线程没有任务时最多保持多少时间终止
// System.out.println(service.getClass());//getclass():获取创造该对象的类型
//2.调用一个线程,需要提供一个实现Runnable接口或Callable接口实现类的对象
service.execute(new NumberThread());//适用于Runnable
// service.submit();//适用于Callable
//3.调用另一个线程
service.execute(new NumberThread1());
//每一个线程都需要一个实现Runnable接口的实现类 列如 NumberThread 和 NumberThread1
service.shutdown();//关线程池
}
}