学习java Day05
多线程
多线程的3种方式
- 继承自Thread类
public class RunTest extends Thread{
public void run(){
for(int i=0;i<20;i++){
System.out.println("RunTest"+i);
}
}
public static void main(String[] args){
RunTest runtest = new RunTest();
runtest.start(); // 调用start() 才是开启多线程,调用run()只会普通调用方法
System.out.println("Main");
}
}
//结果
Main
RunTest0
RunTest1
RunTest2
- 实现Runnable接口**(因为java是单继承推荐使用接口实现)**
public class RunTest implements Runnable{
public void run(){
for(int i=0;i<20;i++){
System.out.println("RunTest"+i);
}
}
public static void main(String[] args){
RunTest runtest = new RunTest();
new Thread(runtest ).start(); // 需要通过创建Thread对象,传入重写Runnable接口的对象开启线程
System.out.println("Main");
}
}
- 实现Callable接口 (重写的是call方法,有返回值,能处理异常)
public class RunTest implements Callable<String>{
public String call() throws Exception {
for(int i=0;i<20;i++){
System.out.println("RunTest"+i);
}
return "结束了";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService ser = Executors.newFixedThreadPool(3);
RunTest runTest = new RunTest();
Future<String> s= ser.submit(runTest);
//接收返回值会进行堵塞,
//String str = s.get();
//System.out.println(str);
System.out.println("main");
ser.shutdown();
}
}
线程的常用方法
- 获取当前线程的名称:
Thread.currentThread().getName() - 让线程阻塞(休眠),不释放锁
Thread.sleep(long mills) // 休眠时间,毫秒值 - 礼让线程 :线程礼让不一定成功(看CPU的调度)
Thread.yield() - 让当前线程停止执行(线程对象的方法)
thread.join(); //直到thread线程执行结束,当前线程再执行
thread.join(long mills); // 等待mills毫秒的时间再执行 - 得到线程状态(线程对象的方法)
Thread.State state = thread.getState() - 设置线程优先级 (优先级从1到10 越高越有可能先执行,默认优先级都为5)(线程对象的方法)
thread.setPriority(); - 设置为守护线程(jvm虚拟机不必等待守护进程执行完毕)(线程对象的方法)
thread.setDaemon(true) // 设为true,默认为false
多线程的并发问题
- 当多个线程对同一个资源进行操作,会出现并发问题
public class BuyTicket implements Runnable{
private int ticket = 10; // 假定总共有10张票
public void run(){
while(ticket>0){
try {
// 增加睡眠时间,提高时间发生可能性
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "买了第" + ticket + "张票");
ticket--;
}
}
public static void main(String[] args){
BuyTicket buy= new BuyTicket (); //定义一个买票窗口
new Thread(buy,"小A").start(); // 定义3个买票人员
new Thread(buy,"小B").start();
new Thread(buy,"小C").start();
}
}
//结果(每次的结果可能不同) 总之结果和我们预期的不一样,出现了票数一样,0票和负票
小A买了第10张票
小B买了第10张票
小C买了第10张票
小A买了第7张票
小B买了第6张票
小C买了第5张票
小A买了第4张票
小B买了第3张票
小C买了第2张票
小A买了第1张票
小B买了第0张票
小C买了第-1张票
处理并发问题的3种方式
每个对象都是一把锁
- 设为同步方法 : 将方法用 synchronized 修饰
public class BuyTicket implements Runnable{
private int ticket = 1000; // 假定总共有10张票
public void run(){
System.out.println(Thread.currentThread().getName());
while(ticket>0){
buy();
}
}
private synchronized void buy() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "买了第" + ticket-- + "张票");
}
public static void main(String[] args){
BuyTicket buy= new BuyTicket (); //定义一个买票窗口
new Thread(buy,"小A").start();
new Thread(buy,"小B").start();
new Thread(buy,"小C").start();
System.out.println("startALl==================");
}
}
- 设定同步代码块: 锁一部分 synchronized(obj){…} 锁对象最好是选择操作的同一个资源对象
private Integer ticket = 10; // 假定总共有10张票
private final Object o; // 锁对象
public BuyTicket2(Object obj){
this.o=obj;
}
public void run(){
System.out.println(Thread.currentThread().getName());
while(true){
synchronized (o){
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "买了第" + ticket-- + "张票");
}else{
break;
}
}
}
}
- 使用Lock对相同资源处理的部分进行锁住
private int ticket = 10; // 假定总共有10张票
// 定义同步锁
private final ReentrantLock lock = new ReentrantLock();
public void run(){
System.out.println(Thread.currentThread().getName());
while(true){
lock.lock();
try{
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "买了第" + ticket-- + "张票");
}else{
break;
}
}finally {
lock.unlock();
}
}
}
线程间通信
- wait() 会释放锁,进入等待池
- notify() 唤醒等待池中该对象锁的一个线程(由cpu决定)
- notifyAll() 唤醒等待池中该对象锁的所有线程,多个线程会去抢夺锁
Lambda表达式
- 只有当接口是函数式接口可以使用(一个接口中只定义了一个抽象方法)
- lambda 其实就是对匿名内部类的简化
public class LambdaTest {
public static void main(String[] args) {
// 匿名内部类实现
lambda ld = new lambda() {
@Override
public void show() {
System.out.println("实现方法。");
}
};
ld.show();
// lambda表达式实现 (这是没有参数的,又只有一行输出语句,最简形式)
ld = ()-> System.out.println("lambda 实现。");
ld.show();
}
}
interface lambda{
void show();
}
普通的多参数带返回值的lambda简化
public class LambdaTest {
public static void main(String[] args) {
lambda ld = new lambda() {
@Override
public String show(int a,int b) {
System.out.println("实现方法。" +a);
System.out.println("实现方法。" +b);
return "匿名内部类";
}
};
System.out.println(ld.show(13,14));
ld = (a ,b)-> {
System.out.println("lambda 实现。" + a);
System.out.println("lambda 实现。" + b);
return "Lambda表达式";
};
System.out.println(ld.show(13,14));
}
}
interface lambda{
String show(int a,int b);
}