Java_问题(1——6)


  1. String 和 StringBuilder、StringBuffer的区别


    (1)运行速度

    String速度最慢,举个例子如下图,虽然第二次输出abcde,但是并非str这个对象改变了,JVM的操作是,第二次进行+这个操作的时候,新建了一个str对象,将原来的abc和新的de拼接起来赋予了新的str,原来的str被GC回收掉了。因此Java对String的操作就是不断新建string类型对象的过程,所以速度很慢。

    而StringBuilder和StringBuffer的对象都是变量,可以直接进行对象的修改,没有其他操作,所以速度快。

    happysneaker.com


    (toString 方法会返回一个“以文本方式表示”此对象的字符串)

    happysneaker.com


    (2)线程安全

    StringBuffer是线程安全的,StringBuilder是线程不安全的。

    因为如果一个StringBuffer的对象再字符串缓冲区被多个线程使用的时候,很多方法可以带有synchronized关键字,所以可以保证线城市安全的,但是StringBuilder没有该关键字,所以不安全。

    但是在单线程的情况下,还是建议使用速度更快的StringBuilder。

    happysneaker.com


2.synchronized与lock的区别

    首先synchronized使用示例:

   示例一: 多窗口售票

Ticket.java

public class Ticket implements Runnable{
    private int num; //票的总数
    private boolean flag = true; //true为有票
    public Ticket(int num){ // 票类构造方法
        this.num=num;
    }
    
    @override
    public void run(){
        while(flag){
            ticket();
        }
    }
    
    private void ticket(){
        if(num<=0){
            flag = false;
            return;
        }
        try{
            Thread.sleep(20); //模拟延时
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        
        System.out.println("Thread.currentThread().getName()+"售出票序列号:"+ num--);
    }
}

MainTest.java

public class MainTest{
    public static void main(String[] args){
        Ticket ticket = new Ticket(5);
        Thread window01 = new Thread(ticket,"窗口01");
        Thread window02 = new Thread(ticket,"窗口02");
        Thread window03 = new Thread(ticket,"窗口03");
        window01.start();
        window02.start();
        window03.start();
    }
}

程序运行结果如下,是错误的:

happysneaker.com

如果是单个窗口售票就不会出现这种问题,多线程的情况下会出现多个窗口争抢共享资源,出现紊乱。

两种解决方法:在ticket()方法前增加一个synchronized关键字,或者把ticket()方法体放入synchronized块。

happysneaker.com

结果是正确的


从这里可以看出在实例方法上面加上synchronized关键字的实现效果跟对整个方法体加上synchronized效果是一样的。 另外一点需要注意加锁的时机也非常重要 ,本示例中ticket()方法中有两处操作容易出现紊乱,一个是在if语句模块,一处是在num–,这两处操作本身都不是原子类型的操作,但是在使用运行的时候需要这两处当成一个整体操作,所以synchronized将整个方法体都包裹在了一起。如若不然,假设num当前值是1,但是窗口01执行到了num–,整个操作还没执行完成,只进行了赋值运算还没进行自减运算,但是窗口02已经进入到了if语句模块,此时num还是等于1,等到窗口02执行到了输出语句的时候,窗口01的num–也已经将自减运算执行完成,这时候窗口02就会输出序列号0的票。再者如果将synchronized关键字加在了run方法上面,这时候的操作不会出现紊乱或者错误,但是这种加锁方式无异于单窗口操作,当窗口01拿到锁进入run()方法之后,必须等到flag为false才会将语句执行完成跳出循环,这时候的num就已经为0了,也就是说票已经被售卖完了,这种方式摒弃了多线程操作,违背了最初的设计原则-多窗口售票。


示例二:懒汉式单例模式

那么根据上面多窗口售票的逻辑我们在getInstance()方法上面加上一个synchronized关键字,给该方法加上锁,加上锁之后可以避免多线程模式下生成多个不同对象,但是同样会带来一个效率问题,因为不管哪个线性进入getInstance()方法都会先获得锁,然后再次释放锁,这是一个方面,另一个方面就是只有在第一次调用getInstance()方法的时候,也就是在if语句块内才会出现多线程并发问题,而我们却索性将整个方法都上锁了。讨论到这里就引出了另外一个问题,究竟是synchronized方法好还是synchronized代码块好呢? 有一个原则就是锁的范围越小越好 ,加锁的目的就是将锁进去的代码作为原子性操作,因为非原子操作都不是线程安全的,因此synchronized代码块应该是在开发过程中优先考虑使用的加锁方式。


Lock:



3.关于Java的this关键字

https://www.cnblogs.com/livterjava/p/4709383.html


4.getName()和Thread.currentThread().getName()的区别:

https://blog.csdn.net/qq_33728573/article/details/60540549


5.为什么我们调用start()方法时会执行run()方法?为什么我们不能直接调用run()方法?他们之间有什么区别?



为什么我们调用start()方法时会执行run()方法?

因为类Thread中的start方法中,调用了Thread中的run方法。顺便说下,类A继承了Tread类,在A中写run方法,就会覆盖掉Thread中的run方法,所以此时调用start方法后,实现的是自己的run方法体里面的代码。

为什么我们不能直接调用run()方法?

如果我们直接调用子线程的run()方法,其方法还是运行在主线程中,代码在程序中是顺序执行的,所以不会有解决耗时操作的问题。所以不能直接调用线程的run()方法,只有子线程开始了,才会有异步的效果。当thread.start()方法执行了以后,子线程才会执行run()方法,这样的效果和在主线程中直接调用run()方法的效果是截然不同的。

start( )与run( )之间有什么区别?

run()方法:在本线程内调用该Runnable对象的run()方法,可以重复多次调用; 
start()方法:启动一个线程,调用该Runnable对象的run()方法,不能多次启动一个线程;


6.Java创建现成的两种方法

 i 通过扩展Thread类来创建线程

ii 通过实现Runnable接口的run()方法来创建线程


i

 MultiThreadDemo.java

public class MultiThreadDemo{
	public static void main(String[] args){
		MultiThread m1 = new MultiThread("Window 1");
		MultiThread m2 = new MultiThread("Window 2");
		MultiThread m3 = new MultiThread("Window 3");
		m1.start();
		m2.start();
		m3.start();
	}
}

MultiThread.java

public class MultiThread extends Thread{
	private int ticket = 100; //每个线程有 100 票
	
	public MultiThread(){}
	public MultiThread(String name){
		super(name);
	}
	
	@Override
	public void run(){
		while(ticket>0){
			System.out.println(ticket-- + "is saled by " + Thread.currentThread().getName());
		}
	}
}

运行结果

happysneaker.com

可以看出三个线程同时执行,并无优先级,同时占用同一资源,互相独立,互不干扰。


ii

MultiThreadDemo.java

public class MultiThreadDemo{
	public static void main(String[] args){
		MultiThread m1 = new MultiThread("Window 1");
		MultiThread m2 = new MultiThread("Window 2");
		MultiThread m3 = new MultiThread("Window 3");
		Thread t1 = new Thread(m1);
		Thread t2 = new Thread(m2);
		Thread t3 = new Thread(m3);
		t1.start();
		t2.start();
		t3.start();
	}
}

MultiThread.java

public class MultiThread implements Runnable{
	private int ticket = 100; //每个线程有 100 票
	private String name;
	
	MultiThread(String name){
		this.name = name;
	}
	
	@Override
	public void run(){
		while(ticket>0){
			System.out.println(ticket-- + "is saled by " + name);
		}
	}
}

运行结果同上。也是线程之间互相独立,互不干扰。也是每个线程都拥有100票。


iii 只能通过实现Runnable接口的方式来实现线程之间的资源共享,因为Java的类是但继承的,而Thread类是一个类啊,哈哈哈哈哈哈。

MultiThreadDemo.java

 public class MultiThreadDemo{
	public static void main(String[] args){
		MultiThread m = new MultiThread();// 仅创建了一个资源

		Thread t1 = new Thread(m);
		Thread t2 = new Thread(m);
		Thread t3 = new Thread(m);
		t1.start();
		t2.start();
		t3.start();
	}
}


MultiThread.java

public class MultiThread implements Runnable{
	private int ticket = 100; //每个线程有 100 票
	
	@Override
	public void run(){
		while(ticket>0){
			System.out.println(ticket-- + "is saled by " + Thread.currentThread());
		}
	}
}

这个就实现了资源共享。


实现Runnable接口相对于扩展Thread类来说,具有无可比拟的优势。这种方式不仅有利于程序的健壮性,使代码能够被多个线程共享,而且代码和数据资源相对独立,从而特别适合多个具有相同代码的线程去处理同一资源的情况。这样一来,线程、代码和数据资源三者有效分离,很好地体现了面向对象程序设计的思想。因此,几乎所有的多线程程序都是通过实现Runnable接口的方式来完成的。

Unity那些事儿
请先登录后发表评论
  • 最新评论
  • 总共0条评论